package com.ywicc.commlibs.udp;

import android.content.Context;
import android.util.Log;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * author : zhouxuan77
 * e-mail : zhouxuan77@163.com
 * time   : 2022/04/01
 * desc   : UDP 相关方法封装
 * version: 1.0
 */
public class UdpUtils {

    private static final String BROADCAST_IP = "255.255.255.255";

    public static final int OBJECT_BROADCAST = 1;

    private final int MULTICAST_PORT = 8886;
    private final int BROADCAST_PORT = 8887;
    private final int UNICAST_PORT = 8888;

    private boolean isBroadcastReceiver = false;
    private boolean isUnicastReceiver = false;

    private final ThreadPoolExecutor threadPool;

    public UdpUtils() {
        // 初始化线程池
        threadPool = new ThreadPoolExecutor(4, 5, 0L,
                TimeUnit.MICROSECONDS, new ArrayBlockingQueue<>(2), new ThreadPoolExecutor.CallerRunsPolicy());
    }

    public void onBroadcastSend(byte[] data) {
        threadPool.execute(new UdpUtilsRunnable(new UdpUtilsRunnableInterface() {
            @Override
            public void run() {
                try {
                    InetAddress inetAddress = InetAddress.getByName(BROADCAST_IP);

                    DatagramSocket datagramSocketSend = new DatagramSocket();
                    DatagramPacket datagramPacket = new DatagramPacket(data, data.length, inetAddress, BROADCAST_PORT);

                    datagramSocketSend.send(datagramPacket);
                    datagramSocketSend.setBroadcast(true);
                    datagramSocketSend.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    public void onBroadcastReceive(UdpUtilsImpl udpUtilsImpl, int intervalTime) {
        threadPool.execute(new UdpUtilsRunnable(new UdpUtilsRunnableInterface() {
            @Override
            public void run() {
                try {
                    DatagramSocket datagramSocket = new DatagramSocket(BROADCAST_PORT);

                    isBroadcastReceiver = true;

                    while (isBroadcastReceiver) {
                        byte[] mmBuffer = new byte[1472];

                        final DatagramPacket datagramPacket = new DatagramPacket(mmBuffer, mmBuffer.length);
                        datagramSocket.receive(datagramPacket);

                        if (udpUtilsImpl != null) {
                            udpUtilsImpl.callBackData(OBJECT_BROADCAST, datagramPacket);
                        }

                        if (intervalTime > 0) {
                            Thread.sleep(intervalTime);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    public void stopBroadcastReceive() {
        isBroadcastReceiver = false;
    }

    public void onUnicastSend(String strHost, byte[] data) {
        threadPool.execute(new UdpUtilsRunnable(new UdpUtilsRunnableInterface() {
            @Override
            public void run() {
                try {
                    InetAddress inetAddress = InetAddress.getByName(strHost);

                    DatagramPacket datagramPacket = new DatagramPacket(data, data.length,
                            inetAddress, UNICAST_PORT);
                    DatagramSocket datagramSocket = new DatagramSocket();
                    datagramSocket.send(datagramPacket);
                    datagramSocket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    public void onUnicastReceive(UdpUtilsImpl udpUtilsImpl, int intervalTime) {
        threadPool.execute(new UdpUtilsRunnable(new UdpUtilsRunnableInterface() {
            @Override
            public void run() {
                try {
                    DatagramSocket server = new DatagramSocket(UNICAST_PORT);
                    while (isUnicastReceiver) {
                        byte[] mmBytes = new byte[1472];
                        final DatagramPacket datagramPacket = new DatagramPacket(mmBytes, mmBytes.length);
                        server.receive(datagramPacket);

                        if (udpUtilsImpl != null) {
                            udpUtilsImpl.callBackData(OBJECT_BROADCAST, datagramPacket);
                        }

                        if (intervalTime > 0) {
                            Thread.sleep(intervalTime);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    public void stopUnicastReceive() {
        isUnicastReceiver = false;
    }

    public interface UdpUtilsImpl {
        void callBackData(int object, DatagramPacket data);
    }

    private interface UdpUtilsRunnableInterface {
        void run();
    }

    public static class UdpUtilsRunnable implements Runnable {

        private final WeakReference<UdpUtilsRunnableInterface> weakInterface;

        public UdpUtilsRunnable(UdpUtilsRunnableInterface runnableInterface) {
            this.weakInterface = new WeakReference<>(runnableInterface);
        }

        @Override
        public void run() {
            weakInterface.get().run();
        }
    }
}
